#include "winsock2.h"
#include "iostream"

#define PORT 8080
#define BUFFER_SIZE 1024

using namespace std;

int main() {

    WSADATA wsaData;    // 用于存储Winsock库的版本信息和系统配置
    SOCKET serverFd, clientFd;  // 服务器监听套接字，客户端连接套接字

    struct sockaddr_in address;    // 定义服务器地址结构体，用于存储IPv4地址和端口信息
    int addrLen = sizeof(address); // 获取地址结构体的长度，用于后续bind()和accept()函数的使用
    char buffer[BUFFER_SIZE] = {0}; // 定义缓冲区，用于存储接收到的数据

    // 初始化Winsock
    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
        cerr << "WinSock初始化失败! 错误码:" << WSAGetLastError() << endl;
        return 1;
    }


    // 1. 创建socket
    if ((serverFd = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) { 
        // 只有创建socket和accept取出socket时，采用INVALID_SOCKET，其他都用SOCKET_ERROR
        cerr << "创建套接字失败，错误码: " << WSAGetLastError() << endl;
        WSACleanup();
        return 1;
    }

    address.sin_family = AF_INET;   // 设置地址族为IPv4， AF_INET6表示IPv6
    address.sin_addr.s_addr = INADDR_ANY;   // 设置地址为INADDR_ANY(0.0.0.0)，即监听所有网络接口
    address.sin_port = htons(PORT);   // 切记，将端口的主机字节序(小端)转换为网络字节序(大端)

    // 2. bind将套接字绑定到一个ip和端口中
    if (bind(serverFd, (struct sockaddr *)&address, sizeof(address)) == SOCKET_ERROR) {
        cerr << "绑定失败，错误码: " << WSAGetLastError() << endl;
        closesocket(serverFd);
        WSACleanup();
        return 1;
    }

    // 3. listen监听客户端发出的连接请求
    if (listen(serverFd, 3) == SOCKET_ERROR) {  // 设置最大监听连接队列的长度为3
        // 会创建两个连接队列，一个是SYN队列，存放收到SYN包，但未完成三次握手的连接；
        // 另一个是ACCEPT队列，存放已经完成三次握手的连接

        // 当客户端发出连接请求时，这个请求会被放入监听套接字的ACCEPT连接队列中，最多保存3个
        // accept函数会从ACCEPT连接队列中取出一个已建立的连接(即已完成三次握手的连接)，并返回一个新的套接字，用于与客户端进行数据交换
        cerr << "监听失败，错误码：" << WSAGetLastError() << endl;
        closesocket(serverFd);
        WSACleanup();
        return 1;
    }

    printf("开始监听端口: %d...\n", PORT);

    // 4. accept阻塞，等待客户端发出连接请求
    // 如果ACCEPT队列为空，accept函数将会阻塞(不占用cpu资源)，直到队列中放入新的连接
    if ((clientFd = accept(serverFd, (struct sockaddr*)&address, &addrLen)) == INVALID_SOCKET) {  // 这里赋值的时候不要写成"=="
        cerr << "accept失败, 错误码：" << WSAGetLastError() << endl;
        closesocket(serverFd);
        WSACleanup();
        return 1;
    }

    printf("客户端连接成功...\n");

    // 5. recv接收数据
    int valread = recv(clientFd, buffer, BUFFER_SIZE, 0);  // 这里操作的是clientFd, accept返回的新的套接字
    // 成功时返回接收到的字节长度（包含换行符）;
    // 失败时返回0，错误返回SOCKET_ERROR;
    // flags=0表示使用recv的默认值，会阻塞直到有数据到达
    if (valread > 0) {
        buffer[valread] = '\0';  
        printf("收到的请求：%s\n", buffer);
    }

    // 6. 发送响应
    const char *response = "HTTP/1.1 200 OK\r\nContent-Length: 17\r\n\r\nHello World!\n\n";
    int bytesSent = send(clientFd, response, strlen(response), 0);  // 这里的flags=0默认会阻塞直到发送成功
    printf("响应已发送，字节： %d\n", bytesSent);

    // 7. 接收客户端关闭响应
    recv(clientFd, buffer, BUFFER_SIZE, 0); // 在实际应用中，要对返回值做判断和处理

    // 8. close关闭套接字
    closesocket(clientFd);
    closesocket(serverFd);
    WSACleanup();

    return 0;
}